package com.currency.authorization.config;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import com.currency.authorization.core.clientdetails.ClientDetailsServiceImpl;
import com.currency.authorization.extension.refresh.PreAuthenticatedUserDetailsService;
import com.currency.authorization.core.userdetails.SysUserDetails;
import com.currency.authorization.core.userdetails.SysUserDetailsServiceImpl;
import com.currency.common.constant.SecurityConstants;
import com.currency.common.result.Result;
import com.currency.common.result.ResultCode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;

/**
 * @author Lan
 */
@EnableAuthorizationServer  //开启功能
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private SysUserDetailsServiceImpl sysUserDetailsService;

    @Resource
    private ClientDetailsServiceImpl clientDetailsServiceImpl;


    /**
     * 配置第三方客户端
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //clients.inMemory()
        //        .withClient("currency")  //第三方客户端名称
        //        .secret(passwordEncoder.encode("123456")) //第三方客户端密钥
        //        .scopes("all") //授权范围
        //        .redirectUris("http://www.baidu.com")
        //        .authorizedGrantTypes("authorization_code", "password","client_credentials", "refresh_token")
        //        .accessTokenValiditySeconds(7 * 24 * 3600) //token有效期
        //        .refreshTokenValiditySeconds(30 * 24 * 3600) //refresh_token有效期
        //        .autoApprove(true); //自动授权
        clients.withClientDetails(clientDetailsServiceImpl);
        //super.configure(clients);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //获取密钥必须要身份认证配置单点登录
        security.tokenKeyAccess("isAuthenticated()");
        super.configure(security);
    }

    /**
     * 配置验证管理器
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
                .accessTokenConverter(jwtAccessTokenConverter())
                .tokenEnhancer(tokenEnhancerChain())
                .tokenServices(tokenServices(endpoints));
    }




    private DefaultTokenServices tokenServices(AuthorizationServerEndpointsConfigurer endpoints) {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(tokenStore()); // tokenStore 来存储我们的 token jwt 存储token
        tokenServices.setSupportRefreshToken(true);
        // 多租户设计
        //tokenServices.setClientDetailsService(clientDetailsService);
        tokenServices.setTokenEnhancer(tokenEnhancerChain());

        // 多用户体系下，刷新token再次认证客户端ID和 UserDetailService 的映射Map
        Map<String, UserDetailsService> userDetailsServiceMap = new HashMap<>();
        userDetailsServiceMap.put(SecurityConstants.ADMIN_CLIENT_ID, sysUserDetailsService); // 系统管理客户端

        // 刷新token模式下，重写预认证提供者替换其AuthenticationManager，可自定义根据客户端ID和认证方式区分用户体系获取认证用户信息
        PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
        provider.setPreAuthenticatedUserDetailsService(new PreAuthenticatedUserDetailsService<>(userDetailsServiceMap));
        tokenServices.setAuthenticationManager(new ProviderManager(Collections.singletonList(provider)));
        return tokenServices;
    }

    private TokenEnhancerChain tokenEnhancerChain() {
        // Token增强
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> tokenEnhancers = new ArrayList<>();
        tokenEnhancers.add(tokenEnhancer());
        tokenEnhancers.add(jwtAccessTokenConverter());
        tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);
        return tokenEnhancerChain;
    }


    private TokenStore tokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter tokenConverter = new JwtAccessTokenConverter();
        //加载私钥
        ClassPathResource classPathResource = new ClassPathResource("currency-admin.jks");
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(classPathResource, "20000125lrl".toCharArray());
        tokenConverter.setKeyPair(keyStoreKeyFactory.getKeyPair("currencyAdmin", "20000125lrl".toCharArray()));
        return tokenConverter;
    }

    @Bean
    public TokenEnhancer tokenEnhancer() {
        return (accessToken, authentication) -> {
            Map<String, Object>  principalInfo = CollectionUtil.newHashMap();
            Object principal = authentication.getUserAuthentication().getPrincipal();
            if(principal instanceof SysUserDetails) {
                SysUserDetails sysUserDetails = (SysUserDetails) principal;
                principalInfo.put("userId", sysUserDetails.getUserId());
                principalInfo.put("username", sysUserDetails.getUsername());
                if (!StringUtils.isEmpty(sysUserDetails.getAuthenticationMethod())) {
                    principalInfo.put("authenticationMethod", sysUserDetails.getAuthenticationMethod());
                }
            }

            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(principalInfo); // token 增强注入设置
            return accessToken;
        };
    }


    /**
     * 自定义认证异常响应数据
     */
    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint() {
        return (request, response, e) -> {
            response.setStatus(HttpStatus.HTTP_OK);
            response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Cache-Control", "no-cache");
            response.getWriter().print(JSONUtil.toJsonStr(Result.failed(ResultCode.CLIENT_AUTHENTICATION_FAILED)));
            response.getWriter().flush();
        };
    }

//    public TokenStore redisTokenStore() {
//        return new RedisTokenStore(redisConnectionFactory);
//    }
}
